Release 10.1A: OpenEdge Development:
Progress Dynamics Basic Development


Server-side validation

Additional validation procedure hooks are designed to be used on the server, where there is free access to the database. This section discusses these entry points. For each of these procedures, remember that the client-side validation entry points described above apply only to a single SDO record. If multiple records are updated, then the client-side validation procedures will be called a number of times. By contrast, the first set of server-side, transaction-level validation routines described below could potentially apply to multiple record updates, and you should write them as FOR EACH blocks on the RowObjUpd table. As a result, when using the procedures defined below, you must write not only a FOR EACH loop on the RowObjUpd table, but also (if the logic applies only to specific operations such as Add, Copy, Update, or Delete) a check on each record within the loop that examines the value of the temp-table field RowObjUpd.RowMod. The field can have a value of A for Add, C for Copy, D for Delete, U for an Updated row, and ‘’ (blank) for the before image of an updated row (the field values as read from the database).

In the case of an update, another standard RowObjUpd field, RowNum, holds a unique row number for each update. The updated row with RowMod = ‘U’ and the before image of the same row with RowMod = ‘’ will have the same RowNum field. Thus, a block of code that must look at both before and after values will generally be of the form:

DEFINE BUFFER OldRowObj FOR RowObjUpd. 
FOR EACH RowObjUpd WHERE RowMod = ‘U’: 
  FIND OldRowObj WHERE RowObjUpd.RowNum = OldRowObj.RowNum AND 
    OldRowObj.RowMod = ‘’. 
  /* Now you can compare values in OldRowObj and RowObjUpd. */ 
END. 

Because of the nature of this SDO-specific logic that you must add to the validation, an alternative set of procedure hooks has been defined for Progress Dynamics to handle updates a row at a time, and with buffer names based on the table names. These alternative hooks are discussed in the "New Progress Dynamics validation procedures" section.

preTransactionValidate

preTransactionValidate always executes on the server, immediately prior to the commencement of a transaction. As such, it can freely read the database, but should do so NO-LOCK. To maximize performance, preTransactionValidate should be restricted to the type of referential integrity check that can be implemented as a simple CAN-FIND statement. For example:

PROCEDURE preTransactionValidate: 
  FOR EACH RowObjUpd: 
    IF NOT CAN-FIND(…) THEN RETURN "…". 
END PROCEDURE. 

Within this loop, use the RowObjUpd.RowMod field to determine the type of change for each row (‘A’dd, ‘C’opy, ‘U’pdate, or ‘D’elete).

Within preTransactionValidate, you can physically alter the values that will be written back to the database, if so desired. That is, any changes made to the records in the temp-table will be seen by later procedures within the transaction. Generally, preTransactionValidate is appropriate for code that makes changes to the updated records, and for code that must do comparisons with existing records in the database, but without making changes to them.

Keep in mind that you do not want to place code here that might need to look at updated and modified records in one table. That is, some records are in the RowObjUpd table as updates and some are unmodified records in the database. For example, if you must total the OrderLine records of an Order in a validation procedure for the OrderLine table, you do not want to total records in the update temp-table and records in the database together. This will complicate your logic unnecessarily. Such logic should be left to the end of the transaction so that all the records are in the database together.

Also, you also do not want to put logic here that does any other database updates. The transaction block is not yet open, and those updates would not be undone if the transaction fails. For example, if the validation logic in an OrderLine SDO must total all the OrderLine records for an Order and adjust the Balance of the associated Customer accordingly, and compare that against the CreditLimit, this validation should not be done in preTransactionValidate.

beginTransactionValidate

The beginTransactionValidate procedure executes immediately after the start of the transaction, but before any database updates have been performed. There is little to do here except perhaps to gain an EXCLUSIVE lock on a parent table to prevent other user sessions from updating the same records (for example, in an OrderLine SDO, lock the Order table). In addition, if it is important to do some other validation before anything is actually written to the database, you should do that here.

For example, if you want to assure that no other user will attempt to make changes to an order (an Order record and its OrderLine records) while you are updating any part of the order, you can read the Order record with an EXCLUSIVE-LOCK at the beginning of the transaction in the OrderLine object. Then, allow the transaction to make changes to one or more OrderLine records.

endTransactionValidate

After all updates have been written to the database, but before the transaction is committed, endTransactionValidate is called. This is the ideal place to perform database cascade operations.

One example: Now that all OrderLine records have been updated, recalculate the total amount owing. If done in the write trigger, then this behavior would execute once for every order line. By placing it in endTransactionValidate, the logic is performed once only, whether one, some, or all of the order lines were updated.

Another example: On delete of a specific entity you might want to conditionally enforce cascade deletion of child records. It is not possible to put conditional cascade deletion into Delete triggers.

postTransactionValidate

The postTransactionValidate procedure executes once the transaction has been committed, and only if the commit was itself successful. This could be a useful place to write log records, if required. Log records are often written from Write triggers and require the triggers to execute within the context of a specific user. If the log were to be written to a database, then postTransactionValidate would have to open a transaction of its own and manage that carefully.


Copyright © 2005 Progress Software Corporation
www.progress.com
Voice: (781) 280-4000
Fax: (781) 280-4095